﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FireStarter.Base;
using FireStarter.Base.BusinessObjectMerger;
using FireStarterModeller.BO;
using FireStarterModeller.Logic;
using FireStarterModeller.Test.BO;
using Habanero.Base;
using Microsoft.Practices.Unity;
using NUnit.Framework;

namespace FireStarterModeller.Test.Logic
{
    [TestFixture]
    public class TestDMSolutionMerger
    {
        #region Setup/Teardown

        [TestFixtureSetUp]
        public virtual void TestFixtureSetup()
        {
            TestUtilsLogic.SetupFixture();
        }

        [SetUp]
        public virtual void SetupTest()
        {
            TestUtilsLogic.SetupTest();
        }

        [TearDown]
        public virtual void TearDownTest()
        {
        }

        #endregion

        [Test]
        public void Test_DMSolution()
        {
            //---------------Set up test pack-------------------
            DMSolution dmSolutionOriginal = TestUtilsShared.CreateSavedDMSolution();
            DMSolution dmSolutionNew = TestUtilsShared.CreateSavedDMSolution();
            BusinessObjectMerger merger = new BusinessObjectMerger(dmSolutionOriginal, null);
            string newName = dmSolutionNew.SolutionName;
            //---------------Execute Test ----------------------
            merger.Import(dmSolutionNew);
            //---------------Test Result -----------------------
            Assert.AreEqual(newName, dmSolutionOriginal.SolutionName);
            Assert.AreNotSame(dmSolutionNew, dmSolutionOriginal);
        }

        [Test]
        public void Test_DMSolution_WithNewClass()
        {
            //---------------Set up test pack-------------------
            DMSolution dmSolutionOriginal = TestUtilsShared.CreateSavedDMSolution();
            DMSolution dmSolutionNew = TestUtilsShared.CreateSavedDMSolution();
            DMClass newClass = TestUtilsDMClass.CreateSavedDMClass(dmSolutionNew);
            BusinessObjectMerger merger = new BusinessObjectMerger(dmSolutionOriginal, null);
            //-----------Assert preconditions-------------------
            Assert.AreEqual(0, dmSolutionOriginal.AllClasses.Count);
            //---------------Execute Test ----------------------
            merger.Import(dmSolutionNew);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, dmSolutionOriginal.AllClasses.Count);
            var toClass = dmSolutionOriginal.AllClasses[0];
            Assert.AreNotSame(newClass, toClass);
            Assert.AreEqual(newClass.ClassNameBO, toClass.ClassNameBO);
        }

        [Test]
        public void Test_IgnoresGenerateCodeProperty()
        {
            //---------------Set up test pack-------------------

            DMSolution dmSolutionOriginal = TestUtilsShared.CreateSavedDMSolution();
            DMClass oldClass = TestUtilsDMClass.CreateSavedDMClass(dmSolutionOriginal);
            oldClass.GenerateCode = false;
            DMSolution dmSolutionNew = TestUtilsShared.CreateSavedDMSolution();
            DMClass newClass = TestUtilsDMClass.CreateSavedDMClass(dmSolutionNew);
            newClass.ClassNameBO = oldClass.ClassNameBO;
            newClass.GenerateCode = true;
            DMSolutionMerger merger = new DMSolutionMerger(dmSolutionOriginal);
            //---------------Execute Test ----------------------
            merger.Import(dmSolutionNew);
            //---------------Test Result -----------------------
            Assert.IsFalse(oldClass.GenerateCode);
        }

        [Test]
        public void Test_IgnoresNeedsRegenerationProperty()
        {
            //---------------Set up test pack-------------------          
            DMSolution dmSolutionOriginal = TestUtilsShared.CreateSavedDMSolution();
            DMClass oldClass = TestUtilsDMClass.CreateSavedDMClass(dmSolutionOriginal);
            oldClass.NeedsRegeneration = true;
            DMSolution dmSolutionNew = TestUtilsShared.CreateSavedDMSolution();
            DMClass newClass = TestUtilsDMClass.CreateSavedDMClass(dmSolutionNew);
            newClass.ClassNameBO = oldClass.ClassNameBO;
            newClass.NeedsRegeneration = false;
            DMSolutionMerger merger = new DMSolutionMerger(dmSolutionOriginal);
            //---------------Execute Test ----------------------
            merger.Import(dmSolutionNew);
            //---------------Test Result -----------------------
            Assert.IsTrue(oldClass.NeedsRegeneration);
        }

        [Test]
        public void Test_MergeAndPostLoadProcessDMClassWithAddedRelationship_ShouldAddRelationshipToMerged()
        {
            //---------------Set up test pack-------------------
            var fromDMRelProp = TestUtilsDMRelationshipProperty.CreateSavedDMRelationshipProperty();
            DMClass fromDMClass = (DMClass)fromDMRelProp.Relationship.ClassDM;

            DMClass toDMClass = TestUtilsDMClass.CreateUnsavedValidDMClass(fromDMClass.Assembly);
            var objectID = TestUtilsDMObjectIdentity.CreateUnsavedValidDMObjectIdentity_NoDMClass();
            objectID.Class = toDMClass;
            var origionalProp = toDMClass.Properties.CreateBusinessObject();
            origionalProp.PropertyName = fromDMRelProp.OwnerPropertyName;
            //---------------Assert Precondition----------------
            //Validate that the newDMClass is setup correctly.
            Assert.IsNotNull(fromDMClass);
            Assert.IsTrue(fromDMClass.Properties.Contains((DMProperty)fromDMRelProp.OwnerProperty));
            var newRelatedDMClass = fromDMRelProp.Relationship.RelatedClass;
            Assert.IsTrue(newRelatedDMClass.Properties.Contains((DMProperty)fromDMRelProp.RelatedProperty));
            Assert.AreSame(fromDMClass.Solution, newRelatedDMClass.Solution);
            Assert.AreEqual(1, fromDMClass.RelationshipsDM.Count);

            //Validate that the origional Class is correctly set up.
            Assert.IsNotNull(toDMClass.ObjectIdentity);
            Assert.AreEqual(0, toDMClass.RelationshipsDM.Count);
            //---------------Execute Test ----------------------
            DMSolutionMerger merger = new DMSolutionMerger(toDMClass.Assembly.Solution);
            merger.Import(fromDMClass.Assembly.Solution);
            DMSolutionPostLoadProcessor processor = new DMSolutionPostLoadProcessor(new AutoResolvingUnityContainer());
            processor.ProcessSolution(fromDMClass.Assembly.Solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, toDMClass.RelationshipsDM.Count);
            var newlyCreatedRelationship = toDMClass.RelationshipsDM.First();
            Assert.AreEqual(fromDMRelProp.Relationship.RelationshipName, newlyCreatedRelationship.RelationshipName);
            Assert.AreEqual(1, newlyCreatedRelationship.RelationshipProperties.Count);
            var newlyCreatedRelProp = newlyCreatedRelationship.RelationshipProperties.First();

            Assert.IsNotNull(newlyCreatedRelProp.Relationship.RelatedClass);
            Assert.IsNotNull(newlyCreatedRelProp.PropertyID);
            var ownerProperty = (DMProperty)newlyCreatedRelProp.OwnerProperty;
            Assert.AreEqual(origionalProp.PropertyID, ownerProperty.PropertyID);
            Assert.AreSame(origionalProp, ownerProperty);
        }

        [Test]
        public void Test_MergeDMClassWithAddedRelationship_ShouldAddRelationshipToMerged()
        {
            //---------------Set up test pack-------------------
            var fromDMRelProp = TestUtilsDMRelationshipProperty.CreateSavedDMRelationshipProperty();
            DMClass fromDMClass = (DMClass)fromDMRelProp.Relationship.ClassDM;

            DMClass toDMClass = TestUtilsDMClass.CreateUnsavedValidDMClass(fromDMClass.Assembly);
            var objectID = TestUtilsDMObjectIdentity.CreateUnsavedValidDMObjectIdentity_NoDMClass();
            objectID.Class = toDMClass;
            var origionalProp = toDMClass.Properties.CreateBusinessObject();
            origionalProp.PropertyName = fromDMRelProp.OwnerPropertyName;
            //---------------Assert Precondition----------------
            //Validate that the newDMClass is setup correctly.
            Assert.IsNotNull(fromDMClass);
            Assert.IsTrue(fromDMClass.Properties.Contains((DMProperty)fromDMRelProp.OwnerProperty));
            var newRelatedDMClass = fromDMRelProp.Relationship.RelatedClass;
            Assert.IsTrue(newRelatedDMClass.Properties.Contains((DMProperty)fromDMRelProp.RelatedProperty));
            Assert.AreSame(fromDMClass.Solution, newRelatedDMClass.Solution);

            //Validate that the origional Class is correctly set up.
            Assert.IsNotNull(toDMClass.ObjectIdentity);
            Assert.AreEqual(0, toDMClass.RelationshipsDM.Count);
            //---------------Execute Test ----------------------
            DMSolutionMerger merger = new DMSolutionMerger(toDMClass);
            merger.Import(fromDMClass);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, toDMClass.RelationshipsDM.Count);
            var newlyCreatedRelationship = toDMClass.RelationshipsDM.First();
            Assert.AreEqual(fromDMRelProp.Relationship.RelationshipName, newlyCreatedRelationship.RelationshipName);
            Assert.AreEqual(1, newlyCreatedRelationship.RelationshipProperties.Count);
            var toCreatedRepProp = newlyCreatedRelationship.RelationshipProperties.First();



            Assert.IsNull(toCreatedRepProp.PropertyID);

            Assert.AreEqual(fromDMRelProp.OwnerPropertyName, toCreatedRepProp.OwnerPropertyName);
            Assert.AreEqual(fromDMRelProp.RelatedPropertyName, toCreatedRepProp.RelatedPropertyName);
        }


        [Test]
        public void Test_MergeSolutionWithAddedRelationship_ShouldAddRelationshipToMerged()
        {
            //---------------Set up test pack-------------------
            var fromDMRelProp = TestUtilsDMRelationshipProperty.CreateSavedDMRelationshipProperty();
            DMClass fromDMClass = (DMClass)fromDMRelProp.Relationship.ClassDM;

            DMClass toDMClass = TestUtilsDMClass.CreateUnsavedValidDMClass(fromDMClass.Assembly);
            toDMClass.ClassNameBO = fromDMClass.ClassNameBO;
            var objectID = TestUtilsDMObjectIdentity.CreateUnsavedValidDMObjectIdentity_NoDMClass();
            objectID.Class = toDMClass;
            var origionalProp = toDMClass.Properties.CreateBusinessObject();
            origionalProp.PropertyName = fromDMRelProp.OwnerPropertyName;
            //---------------Assert Precondition----------------
            //Validate that the newDMClass is setup correctly.
            Assert.IsNotNull(fromDMClass);
            Assert.IsTrue(fromDMClass.Properties.Contains((DMProperty)fromDMRelProp.OwnerProperty));
            var newRelatedDMClass = fromDMRelProp.Relationship.RelatedClass;
            Assert.IsTrue(newRelatedDMClass.Properties.Contains((DMProperty)fromDMRelProp.RelatedProperty));
            Assert.AreSame(fromDMClass.Solution, newRelatedDMClass.Solution);

            //Validate that the origional Class is correctly set up.
            Assert.IsNotNull(toDMClass.ObjectIdentity);
            Assert.AreEqual(0, toDMClass.RelationshipsDM.Count);
            Assert.AreEqual(toDMClass.ClassNameBO, fromDMClass.ClassNameBO);

            Assert.IsNotNull(BusinessObjectMergerSpy.CallFindBusinessObject(toDMClass.Assembly.Classes, fromDMClass));
            //---------------Execute Test ----------------------
            DMSolutionMerger merger = new DMSolutionMerger(toDMClass.Assembly.Solution);
            merger.Import(fromDMClass.Assembly.Solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, toDMClass.RelationshipsDM.Count);
            var newlyCreatedRelationship = toDMClass.RelationshipsDM.First();
            Assert.AreEqual(fromDMRelProp.Relationship.RelationshipName, newlyCreatedRelationship.RelationshipName);
            Assert.AreEqual(1, newlyCreatedRelationship.RelationshipProperties.Count);
            var toCreatedRepProp = newlyCreatedRelationship.RelationshipProperties.First();



            Assert.IsNull(toCreatedRepProp.PropertyID);

            Assert.AreEqual(fromDMRelProp.OwnerPropertyName, toCreatedRepProp.OwnerPropertyName);
            Assert.AreEqual(fromDMRelProp.RelatedPropertyName, toCreatedRepProp.RelatedPropertyName);
        }
        /*
                [Test]
                public void Test_MergeDMClassWithAddedRelationship_ShouldAddRelationshipToMerged()
                {
                    //---------------Set up test pack-------------------
                    var newDMRelProp = TestUtilsDMRelationshipProperty.CreateSavedDMRelationshipProperty();
                    DMClass newDMClass = (DMClass)newDMRelProp.Relationship.ClassDM;

                    DMClass origionalDMClass = TestUtilsDMClass.CreateUnsavedValidDMClass(newDMClass.Assembly);
                    var objectID = TestUtilsDMObjectIdentity.CreateUnsavedValidDMObjectIdentity_NoDMClass();
                    objectID.Class = origionalDMClass;
                    var origionalProp = origionalDMClass.Properties.CreateBusinessObject();
                    origionalProp.PropertyName = newDMRelProp.OwnerPropertyName;
                    //---------------Assert Precondition----------------
                    //Validate that the newDMClass is setup correctly.
                    Assert.IsNotNull(newDMClass);
                    Assert.IsTrue(newDMClass.Properties.Contains((DMProperty)newDMRelProp.OwnerProperty));
                    var newRelatedDMClass = newDMRelProp.Relationship.RelatedClass;
                    Assert.IsTrue(newRelatedDMClass.Properties.Contains((DMProperty)newDMRelProp.RelatedProperty));
                    Assert.AreSame(newDMClass.Solution, newRelatedDMClass.Solution);

                    //Validate that the origional Class is correctly set up.
                    Assert.IsNotNull(origionalDMClass.ObjectIdentity);
                    Assert.AreEqual(0, origionalDMClass.RelationshipsDM.Count);
                    //---------------Execute Test ----------------------
                    DMSolutionMerger merger = new DMSolutionMerger(origionalDMClass);
                    merger.Import(newDMClass);
                    //---------------Test Result -----------------------
                    Assert.AreEqual(1, origionalDMClass.RelationshipsDM.Count);
                    var newlyCreatedRelationship = origionalDMClass.RelationshipsDM.First();
                    Assert.AreEqual(newDMRelProp.Relationship.RelationshipName, newlyCreatedRelationship.RelationshipName);
                    Assert.AreEqual(1, newlyCreatedRelationship.RelationshipProperties.Count);
                    var newlyCreatedRelProp = newlyCreatedRelationship.RelationshipProperties.First();

                    Assert.IsNotNull(newlyCreatedRelProp.PropertyID);
                    var ownerProperty = (DMProperty)newlyCreatedRelProp.OwnerProperty;
                    Assert.AreEqual(origionalProp.PropertyID, ownerProperty.PropertyID);
                    Assert.AreSame(origionalProp, ownerProperty);
                }*/


        [Test]
        public void Test_CustomBORelationshipTraverser_ShouldForceTheTraversingOfTheOwnerPropertyOnTheRelationshipProp()
        {
            //---------------Set up test pack-------------------
            var traverserDM = new BusinessObjectRelationshipTraverserDM();
            var dmProp = new DMRelationshipProperty { OwnerProperty = new DMProperty() };
            var relationship = dmProp.Relationships["Property"];
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            var mustTraverseRelationship = traverserDM.MustTraverseRelationship(dmProp, relationship);
            //---------------Test Result -----------------------
            Assert.IsTrue(mustTraverseRelationship);
        }

        [Test]
        public void Test_MergeRelProp_WhereRelatedToDMProp_ShouldUpdateDMProp()
        {
            //---------------Set up test pack-------------------
            var newDMRelProp = TestUtilsDMRelationshipProperty.CreateSavedDMRelationshipProperty();

            var origDMRelProp = new DMRelationshipProperty();
            //---------------Assert Precondition----------------
            Assert.IsNotNull(newDMRelProp.OwnerProperty);
            Assert.IsNull(origDMRelProp.OwnerProperty);
            //---------------Execute Test ----------------------
            DMSolutionMerger merger = new DMSolutionMerger(origDMRelProp);
            merger.Import(newDMRelProp);
            //---------------Test Result -----------------------
            Assert.IsNotNull(newDMRelProp.PropertyID);
            Assert.IsNotNull(newDMRelProp.OwnerProperty);
        }

        [Test]
        public void Test_MergeDMClass_WhenAddRelationship_ShouldCreateRelWithNoReverseRel()
        {
            //---------------Set up test pack-------------------
            var fromRelProp = TestUtilsDMRelationshipProperty.CreateSavedDMRelationshipProperty();
            var fromRelationship = fromRelProp.Relationship;
            DMClass fromDMClass = (DMClass)fromRelationship.ClassDM;

            DMClass toDMClass = TestUtilsDMClass.CreateUnsavedValidDMClass(fromDMClass.Assembly);
            var objectID = TestUtilsDMObjectIdentity.CreateUnsavedValidDMObjectIdentity_NoDMClass();
            objectID.Class = toDMClass;
            var origionalProp = toDMClass.Properties.CreateBusinessObject();
            origionalProp.PropertyName = fromRelProp.OwnerPropertyName;
            //---------------Assert Precondition----------------
            //Validate that the newDMClass is setup correctly.
            Assert.IsNotNull(fromDMClass);
            Assert.IsTrue(fromDMClass.Properties.Contains((DMProperty)fromRelProp.OwnerProperty));
            var newRelatedDMClass = fromRelationship.RelatedClass;
            Assert.IsTrue(newRelatedDMClass.Properties.Contains((DMProperty)fromRelProp.RelatedProperty));
            Assert.AreSame(fromDMClass.Solution, newRelatedDMClass.Solution);
            Assert.IsNotNull(fromRelationship.ReverseRelationshipName);
            Assert.IsNotNull(fromRelationship.RelatedAssemblyName);
            Assert.IsNotNull(fromRelationship.RelatedClassName);

            //Validate that the origional Class is correctly set up.
            Assert.IsNotNull(toDMClass.ObjectIdentity);
            Assert.AreEqual(0, toDMClass.RelationshipsDM.Count);
            //---------------Execute Test ----------------------
            var merger = new DMSolutionMerger(toDMClass);
            merger.Import(fromDMClass);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, toDMClass.RelationshipsDM.Count);
            var toRelatiosnhip = toDMClass.RelationshipsDM.First();
            Assert.IsNull(toRelatiosnhip.ReverseRelationship);
            Assert.IsNotNull(toRelatiosnhip.ClassDM);
            Assert.IsNull(toRelatiosnhip.RelatedClass);
            Assert.AreEqual(fromRelationship.ReverseRelationshipName, toRelatiosnhip.ReverseRelationshipName);
            Assert.AreEqual(fromRelationship.RelatedAssemblyName, toRelatiosnhip.RelatedAssemblyName);
            Assert.AreEqual(fromRelationship.RelatedClassName, toRelatiosnhip.RelatedClassName);
        }


        [Test]
        public void Test_MergeDMRelationship_WhenAddRelProp_ShouldCreateRelPropWithNoReverseRelProp()
        {
            //---------------Set up test pack-------------------
            var fromDMRelProp = TestUtilsDMRelationshipProperty.CreateSavedDMRelationshipProperty();
            DMRelationship fromRelationship = (DMRelationship)fromDMRelProp.Relationship;
            DMRelationship toRelatiosnhip = new DMRelationship(null);

            //---------------Assert Precondition----------------
            //Validate that the newDMClass is setup correctly.
            Assert.IsNotEmpty(fromRelationship.RelationshipProperties);
            Assert.IsNotNull(fromDMRelProp.OwnerPropertyName);
            Assert.IsNotNull(fromDMRelProp.RelatedPropertyName);

            //Validate that the origional Class is correctly set up.
            Assert.IsEmpty(toRelatiosnhip.RelationshipProperties);

            //---------------Execute Test ----------------------
            var merger = new DMSolutionMerger(toRelatiosnhip);
            merger.Import(fromRelationship);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, toRelatiosnhip.RelationshipProperties.Count);
            var toRelProp = toRelatiosnhip.RelationshipProperties.First();
            Assert.IsNull(toRelProp.ReverseRelationshipProperty);
            Assert.IsNotNull(toRelProp.Relationship);
            Assert.IsNull(toRelProp.OwnerProperty);
            Assert.AreEqual(fromDMRelProp.OwnerPropertyName, toRelProp.OwnerPropertyName);
            Assert.AreEqual(fromDMRelProp.RelatedPropertyName, toRelProp.RelatedPropertyName);

        }
    }

    class BusinessObjectMergerSpy : BusinessObjectMerger
    {
        public BusinessObjectMergerSpy(IMergableBo toBusinessObject, Predicate<IBOProp> ignorePropertyDelegate)
            : base(toBusinessObject, ignorePropertyDelegate)
        {
        }
        public static IMergableBo CallFindBusinessObject(IBusinessObjectCollection businessObjectCollection, IMergableBo mergableBoToFind)
        {
            return BusinessObjectMerger.FindBusinessObject(businessObjectCollection, mergableBoToFind);
        }
    }
}